#ifndef FLVPARSER_H
#define FLVPARSER_H

#include <iostream>
#include <vector>
#include <stdint.h>
#include "Videojj.h"

typedef unsigned long long uint64_t;

class FlvParser
{
public:
    FlvParser();
    virtual ~FlvParser();

    int parse(uint8_t *pBuf, int bufSize, int &usedLength);
    int printInfo();
    int dumpH264(const std::string &path);
    int dumpAAC(const std::string &path);
    int dumpFlv(const std::string &path);

private:
    //Flv header
    class FlvHeader {
    public:
        int version; //版本
        int haveVideo; //是否包含视频
        int haveAudio; //是否包含音频
        int headerSize;//flv header 长度
        /*
         * 指向存放flv头部的 buffer
         * 上面的三个成员指明了 flv 头部的信息，是从 flv 头部中“翻译”得到的
         * 真实的 flv 头部是一个二进制比特串，放在一个 buffer 中，由 pFlvHeader 成员指明
         */
        uint8_t *pFlvHeader;
    };
    
    //Tag 头部
    class TagHeader {
    public:
        TagHeader() : type(0), dataSize(0), timeStamp(0), TSEx(0), streamID(0), totalTS(0) {}
        ~TagHeader() {}
        
        int type; //类型
        int dataSize;//标签 body 的大小
        int timeStamp;//时间戳
        int TSEx;//时间戳的扩展字节
        int streamID;//流的ID，总为 0
        uint32_t totalTS;//完整的时间戳 timeStamp 和 TSEx 拼装
    };
    
    class Tag {
    public:
        Tag() : m_tagHeader(NULL), m_tagData(NULL), m_media(NULL), m_mediaLength(0) {}
        void init(TagHeader *pHeader, uint8_t *pBuf, int leftLength);
        
        TagHeader m_header;
        uint8_t *m_tagHeader; //指向标签头部
        uint8_t *m_tagData; //指向标签 body，原始的 tag data数据
        uint8_t *m_media;//指向标签的元数据，改造后的数据
        int m_mediaLength;//数据长度
    };
    
    class VideoTag : public Tag {
    public:
        VideoTag(TagHeader *pHeader, uint8_t *pBuf, int leftLength, FlvParser *pParser);
        
        int frameType; //帧类型
        int codecID;   //视频编解码类型
        
        int parseH264Tag(FlvParser *pParser);
        int parseH264Configuration(FlvParser *pParser, uint8_t *pTagData);
        int parseNalu(FlvParser *pParser, uint8_t *pTagData);
    };

    class AudioTag : public Tag {
    public:
        AudioTag(TagHeader *pHeader, uint8_t *pBuf, int leftLength, FlvParser *pParser);

        int soundFormat;//音频编码类型
        int soundRate;//采样率
        int soundSize;//精度
        int soundType;//类型

        //aac
        static int aacProfile;//对应 AAC profile
        static int sampleRateIndex; //采样率索引
        static int channelConfig; //通道设置

        int parseAACTag(FlvParser *pParser);
        int parseAudioSpecificConfig(FlvParser *pParser, uint8_t *pTagData);
        int parseRawAAC(FlvParser *pParser, uint8_t *pTagData);
    };

    class MetaDataTag : public Tag {
    public:
        MetaDataTag(TagHeader *pHeader, uint8_t *pBuf, int leftLength, FlvParser *pParser);

        double hexStr2double(const unsigned char *hex, const unsigned int length);
        int parseMeta(FlvParser *pParser);
        void printMeta();

        uint8_t m_amf1_type;
        uint32_t m_amf1_size;
        uint8_t m_amf2_type;
        unsigned char *m_meta;
        unsigned int m_length;

        double m_duration;
        double m_width;
        double m_height;
        double m_videoDataRate;
        double m_framRate;
        double m_videoCodecID;

        double m_audioDataRate;
        double m_audioSampleRate;
        double m_audioSampleSize;
        bool m_stereo;
        double m_audioCodecID;

        std::string m_majorBrand;
        std::string m_minorVersion;
        std::string m_compatibleBrands;
        std::string m_encoder;

        double m_filesSize;
    };

    class FlvStat {
    public:
        FlvStat() : m_metaNum(0), m_videoNum(0), m_audioNum(0), m_maxTimeStamp(0) {}
        ~FlvStat() {}

        int m_metaNum;
        int m_videoNum;
        int m_audioNum;
        int m_maxTimeStamp;
        int m_lengthSize;
    };

    static uint32_t showU32(uint8_t *pBuf);
    static uint32_t showU24(uint8_t *pBuf);
    static uint32_t showU16(uint8_t *pBuf);
    static uint32_t showU8(uint8_t *pBuf);
    static void writeU64(uint64_t &x, int length, int value);
    static uint32_t writeU32(uint32_t n);

    friend class Tag;

private:
    FlvHeader *createFlvHeader(uint8_t *pBuf);
    int destroyFlvHeader(FlvHeader *pHeader);
    Tag *createTag(uint8_t *pBuf, int leftLen);
    int destroyTag(Tag *pTag);
    int stat();
    int statVideo(Tag *pTag);
    int isUserDataTag(Tag *pTag);

private:
    FlvHeader *m_flvHeader;
    std::vector<Tag *> m_tagVector;
    FlvStat m_stat;
    Videojj *m_vjj;

    int m_naluLength;
};

#endif // FLVPARSER_H
